/*
 * Copyright 2022-2023 the original author(https://github.com/wj596)
 * 
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * </p>
 */
package org.jsets.fastboot.flowable.controller;

import java.io.IOException;
import java.io.InputStream;
import java.util.Base64;
import java.util.List;
import java.util.Objects;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.jsets.fastboot.model.BaseResp;
import org.jsets.fastboot.model.DataResp;
import org.jsets.fastboot.model.ListResp;
import org.jsets.fastboot.model.Respond;
import org.jsets.fastboot.flowable.model.dto.FlowModelQuery;
import org.jsets.fastboot.flowable.model.dto.FlowModelSettingParam;
import org.jsets.fastboot.flowable.model.dto.ProcessNode;
import org.jsets.fastboot.flowable.model.entity.CompositeElement;
import org.jsets.fastboot.flowable.model.entity.CompositeModel;
import org.jsets.fastboot.flowable.service.ICompositeElementService;
import org.jsets.fastboot.flowable.service.ICompositeModelService;
import org.jsets.fastboot.util.IoUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.validation.annotation.Validated;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;

/**
 * @author wangjie (https://github.com/wj596)
 * @date 2021.06.25 11:23
 */
@Api(tags = "模型管理")
@RestController
@RequestMapping("/flow/models")
public class CompositeModelController {

	@Autowired
	private ICompositeModelService service;
	@Autowired
	private ICompositeElementService elementService;

	@Value("${flowable.modeler-url:}") 
	private String flowableModelerUrl;
	
	@GetMapping(value="/modeler_url", produces = MediaType.APPLICATION_JSON_VALUE)
	public DataResp<String> getModelerUrl() {
		DataResp<String> respond = DataResp.ok();
		respond.setData(this.flowableModelerUrl);
		return respond;
	}
	
	@PostMapping(value = "/import_bpmn_file")
	@ApiOperation("导入流程模型")
	public Respond importBpmnModel(@RequestParam("file") MultipartFile file, HttpServletRequest request) {
		if (Objects.isNull(file)) {
			throw new IllegalArgumentException("参数[模型文件为空],不能为空");
		}
		InputStream is = null;
		try {
			is = file.getInputStream();
			this.service.importBpmnModelFile(file.getOriginalFilename(), is);
		} catch (IOException e) {
			throw new RuntimeException(e.getMessage(), e);
		} finally {
			IoUtils.closeQuietly(is);
		}
		return BaseResp.ok();
	}
	
	@ApiOperation("新增钉钉模型")
	@PostMapping(value = "/ding_model")
	public Respond insertDingModel(@RequestBody @Validated CompositeModel model) {
		if(this.service.findByKey(model.getModelKey()).isPresent()) {
			throw new IllegalArgumentException("存在KEY为["+model.getModelKey()+"]的模型,请更换");
		}
		this.service.saveDingModel(model);
		return BaseResp.ok();
	}
	
	@ApiOperation("修改钉钉模型")
	@PutMapping(value = "/ding_model")
	public Respond updateDingModel(@RequestBody @Validated CompositeModel model) {
		if (Objects.isNull(model)||Objects.isNull(model.getId())) {
			throw new IllegalArgumentException("实体及其[id]属性,不能为空");
		}
		this.service.saveDingModel(model);
		return BaseResp.ok();
	}
	
	@DeleteMapping(value="/{id}" ,produces = MediaType.APPLICATION_JSON_VALUE)
	@ApiOperation("删除模型")
	@ApiImplicitParams({
		@ApiImplicitParam(name="id", value="主键", required=true, dataType = "String", dataTypeClass = String.class, paramType="path")
	})
	public Respond delete(@PathVariable("id") String id) {
		if (Objects.isNull(id)) {
			throw new IllegalArgumentException("参数[id],不能为空");
		}
		this.service.deleteById(id);
		return BaseResp.ok();
	}
	
	@ApiOperation("设置模型属性")
	@PutMapping(value = "/setting")
	public Respond settingModel(@RequestBody @Validated FlowModelSettingParam setting) {
		this.service.settingModel(setting);
		return BaseResp.ok();
	}
	
	@GetMapping(produces = MediaType.APPLICATION_JSON_VALUE)
	@ApiOperation("查询模型")
	@ApiImplicitParams({
		 @ApiImplicitParam(name="page_no", value="当前页码", required=true, dataType = "Integer", dataTypeClass = Integer.class, paramType="query")
		,@ApiImplicitParam(name="page_size", value="每页条数", required=true, dataType = "Integer", dataTypeClass = Integer.class, paramType="query")
		,@ApiImplicitParam(name="key", value="KEY", required=false, dataType = "String", dataTypeClass = String.class, paramType="query")
		,@ApiImplicitParam(name="name", value="名称", required=false, dataType = "String", dataTypeClass = String.class,  paramType="query")
	})
	public ListResp<CompositeModel> selectPage(
			@RequestParam(name = "page_no", required = true) Integer pageNo,
			@RequestParam(name = "page_size", required = true) Integer pageSize,
			@RequestParam(name = "key", required = false) String key,
			@RequestParam(name = "name", required = false) String name,
			@RequestParam(name = "version", required = false) Integer version) {

		FlowModelQuery criteria = new FlowModelQuery()
				.setCurrent(pageNo)
				.setSize(pageSize)
				.setKey(key)
				.setName(name)
				.setVersion(version);
		ListResp<CompositeModel> respond = ListResp.ok();
		Page<CompositeModel> result = this.service.selectPage(criteria);
		respond.setData(result.getRecords());
		respond.setTotalCount(result.getTotal());
		return respond;
	}
	
    @ApiOperation("获取模型")
    @ApiImplicitParams({
    	@ApiImplicitParam(name = "id", value = "主键", required = true, dataType = "String", dataTypeClass = String.class, paramType = "path"),
    })
    @GetMapping(value = "/{id}", produces = MediaType.APPLICATION_JSON_VALUE)
    public DataResp<CompositeModel> get(@PathVariable("id") String id) {
    	CompositeModel compositeModel = this.service.getById(id);
		List<ProcessNode> processNodes = this.service.iterateFlowElements(compositeModel.getActReModelId());
		List<CompositeElement> compositeElements = Lists.newArrayList();
		processNodes.forEach(t->{
			CompositeElement ce = this.elementService.getByModelId(id, t.getId());
			if(Objects.isNull(ce)) {
				ce = new CompositeElement();
				ce.setModelId(compositeModel.getId());
				ce.setDeploymentId(compositeModel.getDeploymentId());
				ce.setTaskDefKey(t.getId());
				ce.setTaskDefName(t.getName());
				ce.setTaskType(t.getType());
				ce.setProcessKey(compositeModel.getModelKey());
			}
			compositeElements.add(ce);
		});
		compositeModel.setElements(compositeElements);
    	
    	DataResp<CompositeModel> respond = DataResp.ok();
		respond.setData(compositeModel);
		return respond;
    }
    
    @ApiOperation("获取流程图")
    @ApiImplicitParams({
    	@ApiImplicitParam(name = "deployment_id", value = "部署主键", required = true, dataType = "String", dataTypeClass = String.class, paramType = "path"),
    })
    @GetMapping(value = "/{deployment_id}/image", produces = MediaType.APPLICATION_JSON_VALUE)
    public DataResp<String> getImageByDeploymentId(@PathVariable("deployment_id") String deploymentId, HttpServletResponse response) {
    	InputStream input = this.service.getImageByDeploymentId(deploymentId);
		DataResp<String> respond = DataResp.ok();
		respond.setData(toBase64(input));
		return respond;
    }
    
    @ApiOperation("获取高亮流程图")
    @ApiImplicitParams({
    	@ApiImplicitParam(name = "id", value = "主键", required = true, dataType = "String", dataTypeClass = String.class, paramType = "path"),
    })
    @GetMapping(value = "/{instance_id}/highlight_image")
    public DataResp<String> getHighlightImage(@PathVariable("instance_id") String instanceId, HttpServletResponse response) {
    	InputStream input = this.service.getHighlightImage(instanceId);
		DataResp<String> respond = DataResp.ok();
		respond.setData(toBase64(input));
		return respond;
    }
    
	protected String toBase64(final InputStream inputStream) {
		try {
            byte[] arr = new byte[inputStream.available()];
            inputStream.read(arr);
            String str = Base64.getEncoder().encodeToString(arr);
            return str;
		} catch (IOException e) {
			throw new RuntimeException(e.getMessage(), e);
		} finally {
			IoUtils.closeQuietly(inputStream);
		}
	}
}